package nl.Viewer.ViewManager.Views;

import java.util.concurrent.ConcurrentHashMap;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLJPanel;
import javax.media.opengl.glu.GLU;

import nl.Viewer.SerializedObjects.Parameter;
import nl.Viewer.ViewManager.Controllers.GenericController;
import nl.Viewer.ViewManager.Objects.RendererHelperFunctions;

/**
 * This is the implementation of a View to show the orientation of a plane in a bounding box
 * 
 * @author Jeroen Verschuur
 * 
 */

public class JOGLBoxOrientationView extends GenericView implements GLEventListener {
   private static final long serialVersionUID            = -2958949544243327703L;

   /********************************************************************************************************************
    * 
    * Interaction properties
    * 
    */

   /* viewing parameters */
   protected Parameter       sourceObjectBoundsParameter = null;
   protected Parameter       eyeParameter                = null;
   protected Parameter       viewUpParameter             = null;
   protected Parameter       centerParameter             = null;
   protected Parameter       viewAngleParameter          = null;
   protected Parameter       clippingRangeParameter      = null;
   protected Parameter       azimuthParameter            = null;
   protected Parameter       elevationParameter          = null;
   protected Parameter       twistParameter              = null;
   protected Parameter       zoomParameter               = null;
   protected Parameter       viewSizeParameter           = null;

   protected double[]        sourceObjectBoundsValue     = new double[] { 0, 0, 0, 0, 0, 0 };
   protected double[]        eyeValue                    = new double[] { 0, 0, 0 };
   protected double[]        viewUpValue                 = new double[] { 0, 0, 0 };
   protected double[]        centerValue                 = new double[] { 0, 0, 0 };
   protected double          viewAngleValue              = 30;
   protected double[]        clippingRangeValue          = new double[] { 0, 0 };
   protected double          azimuthValue                = 0;
   protected double          elevationValue              = 0;
   protected double          twistValue                  = 0;
   protected double          zoomValue                   = 1;

   private boolean           parametersRetrieved         = false;

   /********************************************************************************************************************
    * 
    */

   protected double          aspect                      = 1.0;
   protected GLU             glu                         = new GLU();



   public JOGLBoxOrientationView( GenericController parent ) {
      super( parent );
      this.renderPanel = new GLJPanel( getCapabilities() );

      parent.getDataModel().addDataStoreModelChangeListener( this );
      parent.getDataModel().addParameterModelChangeListener( this );

      ( (GLJPanel)renderPanel ).addGLEventListener( this );
   }

   public static GLCapabilities getCapabilities() {
      GLCapabilities c = new GLCapabilities();
      c.setDoubleBuffered( true );
      return c;
   }

   /**
    * Called by drawable to indicate mode or device has changed
    */
   public void displayChanged( GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged ) {
   }

   /**
    * Called after OpenGL is init'ed
    */
   public void init( GLAutoDrawable drawable ) {
      GL gl = drawable.getGL();
      // set erase color
      gl.glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); // white
      // set drawing color and point size
      gl.glColor3f( 0.0f, 0.0f, 0.0f );
      gl.glPointSize( 1.0f ); // a 'dot' is 4 by 4 pixel

      // enable alpha mask (import from gif sets alpha bits)
      gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA );
      gl.glEnable( GL.GL_BLEND );
   }

   /**
    * Called to indicate the drawing surface has been moved and/or resized
    */
   public void reshape( GLAutoDrawable drawable, int x, int y, int width, int height ) {
      GL gl = drawable.getGL();
      gl.glViewport( 0, 0, width, height );
      gl.glMatrixMode( GL.GL_PROJECTION );
      gl.glLoadIdentity();

      aspect = (double)width / (double)height;
      glu.gluOrtho2D( 0.0, width, 0.0, height );
   }

   public void display() {
      ( (GLJPanel)renderPanel ).display();
   }

   /**
    * Called by drawable to initiate drawing
    */
   public void display( GLAutoDrawable drawable ) {
      GL gl = drawable.getGL();
      gl.glClear( GL.GL_COLOR_BUFFER_BIT );

      if( sourceObjectBoundsParameter != null ) {
         // 3D if interacting
         gl.glMatrixMode( GL.GL_PROJECTION );
         gl.glLoadIdentity();
         glu.gluPerspective( viewAngleValue / zoomValue, aspect, 0.0001/* clippingRangeValue[0] */, 10000.0 /* clippingRangeValue[1] */);

         gl.glMatrixMode( GL.GL_MODELVIEW );
         gl.glLoadIdentity();

         glu.gluLookAt( eyeValue[0], eyeValue[1], eyeValue[2], centerValue[0], centerValue[1], centerValue[2],
               viewUpValue[0], viewUpValue[1], viewUpValue[2] );

         gl.glTranslated( centerValue[0], centerValue[1], centerValue[2] );

         gl.glRotated( -twistValue, 0, 0, 1 );
         gl.glRotated( elevationValue, 1, 0, 0 );
         gl.glRotated( -azimuthValue, 0, 1, 0 );

         gl.glTranslated( -centerValue[0], -centerValue[1], -centerValue[2] );

         RendererHelperFunctions.drawBox( gl, sourceObjectBoundsValue[0], sourceObjectBoundsValue[1],
               sourceObjectBoundsValue[2], sourceObjectBoundsValue[3], sourceObjectBoundsValue[4],
               sourceObjectBoundsValue[5] );
      }

      gl.glFlush();
      ( (GLJPanel)renderPanel ).swapBuffers();
   }

   /*
    * Parameter methods
    * 
    */
   public void parameterValueChanged( Parameter p ) {
      synchronized( this ) {
         synchronizeParameterValues();
      }
   }

   public void dataStoreChanged( boolean newDataStore ) {
      synchronized( this ) {
         getAllParameters();
      }
   }

   private void getAllParameters() {
      if( parametersRetrieved )
         return;

      ConcurrentHashMap<String, Parameter> parameters = parent.getParameters();

      sourceObjectBoundsParameter = parameters.get( "SourceObjectBounds" );
      eyeParameter = parameters.get( "CameraPosition" );
      viewUpParameter = parameters.get( "CameraViewUp" );
      centerParameter = parameters.get( "CameraFocalPoint" );
      viewAngleParameter = parameters.get( "CameraViewAngle" );
      clippingRangeParameter = parameters.get( "CameraClippingRange" );
      azimuthParameter = parameters.get( "Azimuth" );
      elevationParameter = parameters.get( "Elevation" );
      twistParameter = parameters.get( "Twist" );
      zoomParameter = parameters.get( "Zoom" );
      viewSizeParameter = parameters.get( "ViewSize" );

      parametersRetrieved = true;
   }

   private void synchronizeParameterValues() {
      if( sourceObjectBoundsParameter != null )
         sourceObjectBoundsValue = GenericView.objectToDoubleArray( sourceObjectBoundsParameter.getParameterValue() );

      if( eyeParameter != null )
         eyeValue = GenericView.objectToDoubleArray( eyeParameter.getParameterValue() );

      if( viewUpParameter != null )
         viewUpValue = GenericView.objectToDoubleArray( viewUpParameter.getParameterValue() );

      if( centerParameter != null )
         centerValue = GenericView.objectToDoubleArray( centerParameter.getParameterValue() );

      if( viewAngleParameter != null )
         viewAngleValue = GenericView.objectToDouble( viewAngleParameter.getParameterValue() );

      if( clippingRangeParameter != null )
         clippingRangeValue = GenericView.objectToDoubleArray( clippingRangeParameter.getParameterValue() );

      if( azimuthParameter != null )
         azimuthValue = GenericView.objectToDouble( azimuthParameter.getParameterValue() );

      if( elevationParameter != null )
         elevationValue = GenericView.objectToDouble( elevationParameter.getParameterValue() );

      if( twistParameter != null )
         twistValue = GenericView.objectToDouble( twistParameter.getParameterValue() );

      if( zoomParameter != null )
         zoomValue = GenericView.objectToDouble( zoomParameter.getParameterValue() );
      
      /*
       * azimuthValue[0] = 0; elevationValue[0] = 0; twistValue[0] = 0; zoomValue[0] = 1.0; // when zooming on Paraview
       * object, Paraview will change the viewAngle
       */
      
      super.synchronizeViews();
      
   }
}
